﻿/**
 * This is a part of MCServe, which is available under the Apache license 2.0.
 * With this code, you may do whatever you want as long as that is not in conflict with this license.
 * In addition, you should have a reference to the original project to include (http://mcserve.codeplex.com).
 * 
 * Author       : Lars Krämer ( Xe77a )
 * Website      : http://www.lkraemer.de
 * Project      : MCServe (http://mcserve.codeplex.com)
 * Copyright    : Copyright (c) 2013 lkraemer.de
 * License      : http://mcserve.codeplex.com/license
 * Description  : This is a part of the core functionality of the server
 */

using MCServeCore.Components;
using MCServeCore.PlayerObjects;
using MCServeLib.Objects;
using MCServeLib.Types;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace MCServeCore
{
    public class GameServer
    {
        // File Paths
        public static ServerPaths Paths = new ServerPaths();
        // ServerConsole Class
        public static ServerConsole Console = new ServerConsole();
        // ServerBot Player
        public static ServerPlayer ServerPlayerBot = new ServerPlayer("ServerBot", "Server Team");
        // Player-List
        public static PlayerList PlayerList = new PlayerList();
        // Server Properties
        public static ServerProperties ServerProperties = new ServerProperties();
        // Command Executing List
        public static Dictionary<String, Delegate> CommandList = new Dictionary<String, Delegate>();
        // WhiteList
        public static ServerWhiteList WhiteList = new ServerWhiteList();
        // Banned Players
        public static ServerBannedPlayersList BannedPlayers = new ServerBannedPlayersList();
        // Banned Ips
        public static ServerIpList BannedIps = new ServerIpList();
        // Ops
        public static ServerOpsList Ops = new ServerOpsList();
        // GameServer Thread
        private Thread ServerThread = null;
        // TCP Listener
        private TcpListener Listener = null;
        // Game Database
        public static Database GameDatabase = new Database(Paths.Get(MCServePaths.FOLDER_DATABASE) + "\\MCServe.sdf");

        private Boolean _IsReady = false;
        public Boolean IsReady
        {
            /**
             * Returns true, if the server is ready to run.
             */
            get
            {
                return _IsReady;
            }
            set
            {
                _IsReady = value;
            }
        }

        private Boolean _IsRunning = false;
        public Boolean IsRunning
        {
            /**
             * Returns true, while the server runs.
             */
            get
            {
                return _IsRunning;
            }
            set
            {
                _IsRunning = value;
            }
        }

        private AppHostTypes _ClientType = AppHostTypes.UNKNOWN;
        public AppHostTypes ClientType
        {
            get
            {
                return _ClientType;
            }
            set
            {
                _ClientType = value;
            }
        }

        public GameServer(Object client)
        {
            _IsReady = false;
            _IsRunning = false;
            _ClientType = (AppHostTypes)client;
        }

        public void Initialize()
        {
            Boolean ResultReload = Reload();

            IsReady = true;
            Console.SendMessage("MCServe Initialization complete", ServerConsole.OutputTarget.Console, ServerConsole.OutputTypes.Information);

            try
            {
                Console.SendMessage("MCServe GameServer starts in " + ClientType + ".", ServerConsole.OutputTarget.Console, ServerConsole.OutputTypes.Information);
            }
            catch (Exception err)
            {
                Console.SendMessage(err.Message, ServerConsole.OutputTarget.ErrorLogAndConsole, ServerConsole.OutputTypes.Error);
            }
        }

        public Boolean SetState(Boolean NewState)
        {
            IsRunning = NewState;

            if (NewState)
            {
                Startup();
                Console.SendMessage("MCServe is launched", ServerConsole.OutputTarget.MessageLogAndConsole, ServerConsole.OutputTypes.Message);
            }
            else
            {
                Shutdown();
                Console.SendMessage("MCServe is stopped", ServerConsole.OutputTarget.MessageLogAndConsole, ServerConsole.OutputTypes.Message);
            }

            return true;
        }

        private Boolean Startup()
        {
            GameServer.PlayerList.Add(GameServer.ServerPlayerBot);

            Console.SendMessage("Listen to " + Convert.ToString(ServerProperties.GetValue("ServerIp")) + ":" + Convert.ToString(ServerProperties.GetValue("ServerPort")), ServerConsole.OutputTarget.MessageLogAndConsole, ServerConsole.OutputTypes.Information);
            Listener = new TcpListener(IPAddress.Parse(Convert.ToString(ServerProperties.GetValue("ServerIp"))), Convert.ToInt32(ServerProperties.GetValue("ServerPort")));

            ServerThread = new Thread(GameLoop);
            ServerThread.Start();

            IsRunning = true;

            return true;
        }

        private Boolean Shutdown()
        {
            ServerThread.Join();
            IsRunning = false;

            GameServer.PlayerList.Remove(GameServer.ServerPlayerBot.Guid);

            return true;
        }

        public Boolean Reload()
        {
            #region # Load data #

            #endregion

            #region # Folder-Check #

            if (!Directory.Exists(GameServer.Paths.Get(MCServePaths.FOLDER_PLUGINS)))
            {
                #region # PLUGINS-FOLDER #

                Directory.CreateDirectory(GameServer.Paths.Get(MCServePaths.FOLDER_PLUGINS));

                #endregion
            }

            if (!Directory.Exists(GameServer.Paths.Get(MCServePaths.FOLDER_SETTINGS)))
            {
                #region # SETTINGS-FOLDER #

                Directory.CreateDirectory(GameServer.Paths.Get(MCServePaths.FOLDER_SETTINGS));

                #endregion
            }

            if (!Directory.Exists(GameServer.Paths.Get(MCServePaths.FOLDER_WORLDS)))
            {
                #region # WORLDS-FOLDER #

                Directory.CreateDirectory(GameServer.Paths.Get(MCServePaths.FOLDER_WORLDS));

                #endregion
            }

            if (!Directory.Exists(GameServer.Paths.Get(MCServePaths.FOLDER_LOGS)))
            {
                #region # LOGS-FOLDER #

                Directory.CreateDirectory(GameServer.Paths.Get(MCServePaths.FOLDER_LOGS));

                #endregion
            }

            if (!Directory.Exists(GameServer.Paths.Get(MCServePaths.FOLDER_DATABASE)))
            {
                #region # DATABASE-FOLDER #

                Directory.CreateDirectory(GameServer.Paths.Get(MCServePaths.FOLDER_DATABASE));

                #endregion
            }

            #endregion

            #region # Files-Check #

            if (!File.Exists(GameServer.Paths.Get(MCServePaths.LOG_ERRORS)))
            {
                #region # ERRORS.LOG #

                StreamWriter ServerFile = new StreamWriter(GameServer.Paths.Get(MCServePaths.LOG_ERRORS));
                ServerFile.Close();

                #endregion
            }

            if (!File.Exists(GameServer.Paths.Get(MCServePaths.LOG_WARNING)))
            {
                #region # WARNING.LOG #

                StreamWriter ServerFile = new StreamWriter(GameServer.Paths.Get(MCServePaths.LOG_WARNING));
                ServerFile.Close();

                #endregion
            }

            if (!File.Exists(GameServer.Paths.Get(MCServePaths.LOG_MESSAGES)))
            {
                #region # MESSAGES.LOG #

                StreamWriter ServerFile = new StreamWriter(GameServer.Paths.Get(MCServePaths.LOG_MESSAGES));
                ServerFile.Close();

                #endregion
            }

            if (!File.Exists(GameServer.Paths.Get(MCServePaths.LOG_SERVER)))
            {
                #region # SERVER.LOG #

                StreamWriter ServerFile = new StreamWriter(GameServer.Paths.Get(MCServePaths.LOG_SERVER));
                ServerFile.Close();

                #endregion
            }

            if (!File.Exists(GameServer.Paths.Get(MCServeLib.Types.MCServePaths.SETTINGS)))
            {
                #region # PROPERTIES FILE #

                /**
                 * Check the current Data
                 */
                // IP-Adress
                IPHostEntry iphostentry = Dns.GetHostEntry(Dns.GetHostName());
                String TheIp = "";
                Boolean HasIp = false;
                foreach (IPAddress ipaddress in iphostentry.AddressList)
                {
                    if (ipaddress.AddressFamily == AddressFamily.InterNetwork)
                    {
                        HasIp = true;
                        TheIp = ipaddress.ToString();
                    }
                    else if (ipaddress.AddressFamily != AddressFamily.InterNetwork && HasIp == false)
                    {
                        HasIp = true;
                        TheIp = ipaddress.ToString();
                    }
                }
                GameServer.ServerProperties.Change(GameServer.ServerProperties.GetGuid("ServerIp"), "ServerIp", TheIp);

                StreamWriter testjson = new StreamWriter("server.json");
                testjson.Write(JsonConvert.SerializeObject(GameServer.ServerProperties));
                testjson.Close();

                #endregion
            }

            #endregion

            #region # Load Environment #

            // Game Database
            Console.SendMessage("Try to initialize the GameDatabase", ServerConsole.OutputTarget.Console, ServerConsole.OutputTypes.Information);
            DatabaseInformation ResultDatabase = GameDatabase.Init();
            if (ResultDatabase.Result == false)
            {
                Console.SendMessage("There was an error in the initialization of the database : '" + ResultDatabase.Description + "'", ServerConsole.OutputTarget.Console, ServerConsole.OutputTypes.Information);
            }
            else
            {
                Console.SendMessage("MCServe Database Initialization complete", ServerConsole.OutputTarget.Console, ServerConsole.OutputTypes.Information);
            }

            // Content of the database
            if (!GameDatabase.AlreadyExists)
            {
                DatabaseResult DBResults = null;
                Console.SendMessage("MCServe Core Database doesn't exist and is regenerated.", ServerConsole.OutputTarget.MessageLog, ServerConsole.OutputTypes.Information);

                // Table 'users'
                Console.SendMessage("Database\t-> Create table 'users'", ServerConsole.OutputTarget.MessageLog, ServerConsole.OutputTypes.Information);
                DBResults = GameDatabase.Query("CREATE TABLE users(id int NOT NULL IDENTITY (1,1) PRIMARY KEY, guid nvarchar(255), type nvarchar(255), username nvarchar(255), clan nvarchar(255), cankick bit, canban bit)");
                Console.SendMessage("        \t\\-> Result : " + DBResults.Result, ServerConsole.OutputTarget.MessageLog, ServerConsole.OutputTypes.Information);

                // Table 'plugins'
                Console.SendMessage("Database\t-> Create table 'plugins'", ServerConsole.OutputTarget.MessageLog, ServerConsole.OutputTypes.Information);
                DBResults = GameDatabase.Query("CREATE TABLE plugins(id int NOT NULL IDENTITY (1,1) PRIMARY KEY, guid nvarchar(255), title nvarchar(255))");
                Console.SendMessage("        \t\\-> Result : " + DBResults.Result, ServerConsole.OutputTarget.MessageLog, ServerConsole.OutputTypes.Information);
            }


            #endregion

            return true;
        }

        internal void GameLoop()
        {
            if (IsReady)
            {
                while (IsRunning)
                {
                    Thread.Sleep(1);
                }
            }
            else {
                Console.SendMessage("The server isn't ready to start", ServerConsole.OutputTarget.ErrorLogAndConsole, ServerConsole.OutputTypes.Error);
            }
        }
    }
}
